import { Asset, assetManager } from 'cc';
import { SceneAssetInfo, SceneInfo, StageInfo } from '../declarations';
import { Dictionary, Logger } from '../../../../wing/assets/src';
import { app } from '../application';
import { ResLoader } from '../manager';

/** 临时资源缓存器 */
export class AssetCache extends Dictionary<Asset> {
    /** 释放所有临时资源（请保证资源没有被引用） */
    releaseAll() {
        this.each((_, v) => assetManager.releaseAsset(v));
        this.clear();
    }
}

/**
 * - 作者: DoooReyn
 * - 日期: 2023.10.31
 * - 名称: BaseStage
 * - 描述: 舞台基类
 */
export abstract class BaseStage extends AssetCache implements StageInfo {
    /** 场景信息 */
    public abstract get sceneInfo(): SceneInfo;

    /** 日志组件 */
    public get logger() {
        return Logger.getLogger(this.sceneInfo.scene);
    }

    /** 缓存 */
    public get cache(): AssetCache {
        return this;
    }

    /** 卸载时调用，有需要时子类可以重写 */
    public async onUnload(): Promise<void> {
        // 释放资源和舞台所属的资源包
        this.cache.releaseAll();
        if (this.sceneInfo.bundle !== 'resources') {
            const bundle = assetManager.getBundle(this.sceneInfo.bundle);
            if (bundle) {
                // TODO 考虑是否需要释放资源包内的所有资源
                // bundle.releaseAll();
                assetManager.removeBundle(bundle);
            }
        }
    }

    public async onLoad(): Promise<void> {
        // 注册与加载相关的监听
        const listener = app.resLoader.getListener(this.sceneInfo.scene);
        listener && listener.on(ResLoader.EventType.LoadStart, this.onLoadStart, this);
        listener && listener.on(ResLoader.EventType.LoadProgress, this.onLoadProgress, this);
        listener && listener.on(ResLoader.EventType.LoadError, this.onLoadError, this);
        listener && listener.on(ResLoader.EventType.LoadEnd, this.onLoadEnd, this);
        // 展示加载页
        await app.view.bigLoading.showLoading(this.sceneInfo.bigLoading, this.sceneInfo.bundle);
        // 启动资源加载
        await app.resLoader.loadOneByOne(this.sceneInfo.assetsToLoad, this.sceneInfo.scene);
    }

    /** 加载开始时调用 */
    protected onLoadStart(): void {
        this.logger.debug('舞台加载开始');
        app.view.bigLoading.onLoadStart();
    }

    /** 
     * 加载进度更新时调用
     * @param info 场景资源信息
     * @param asset 当前资源
     * @param finished 当前量
     * @param total 总量
     */
    protected onLoadProgress(info: SceneAssetInfo, asset: Asset, finished: number, total: number): void {
        this.logger.debug(`舞台加载中: ${finished}/${total}`);
        app.view.bigLoading.onLoadProgress(finished, total);
        if (asset && !info.cached) {
            // 不需要缓存意味着可以被释放
            this.cache.add(asset.uuid, asset);
        }
    }

    /** 
     * 加载出现错误时调用 
     * @param err 错误信息
     */
    protected onLoadError(err: any): void {
        this.logger.error('舞台加载错误', err);
        app.view.bigLoading.onLoadError(err);
    }

    /** 加载完成时调用 */
    protected async onLoadEnd(): Promise<void> {
        this.logger.debug('舞台加载完成！');
        // 加载完成后调用
        await app.view.bigLoading.onLoadEnd();
        // 注销与加载相关的监听
        app.resLoader.removeListener(this.sceneInfo.scene);
    }

    /** 舞台准备就绪 */
    public async onReady() {
        // 加载背景图片
        const { background, bundle } = this.sceneInfo;
        if (background) {
            app.view.background.fitMode = background.fitMode ?? 0;
            await app.view.background.changeByPath(background.path, bundle);
        }
        // 打开首屏页面
        await app.view.screen.add(this.sceneInfo.screen, this.sceneInfo.bundle, null);
        // 关闭加载页
        await app.view.bigLoading.removeLoading();
    }

    public abstract onEnter(): Promise<void>;
    public abstract onExit(): Promise<void>;
    public abstract onTransitionEnter(): Promise<void>;
    public abstract onTransitionExit(): Promise<void>;
}
